///调用函数步骤
/// 1.在栈上保存当前的内存位置
/// 2.自增栈指针
/// 3.把当前内存位置设置为预期的内存地址
/// 函数返回
/// 1.自减栈指针
/// 2.从栈中取回调用前的内存地址
/// 3.把内存位置设置为预期内存地址
struct CPU {
    position_in_memory: usize, //内存位置，程序计数器
    registers: [u8; 16],       //16个寄存器
    memory: [u8; 4096],        //4k内存
    stack: [u16; 16],          //栈最大高度为16，超过就溢出
    stack_pointer: usize,      //栈指针
}
impl CPU {
    //如果从内存读会很复杂
    fn read_opcode(&self) -> u16 {
        let p = self.position_in_memory;
        let op_byte1 = self.memory[p] as u16;
        let op_byte2 = self.memory[p + 1] as u16;
        op_byte1 << 8 | op_byte2 //先转换为U16,防止移位后高位为0，再拼接
    }

    fn run(&mut self) {
        loop {
            let opcode = self.read_opcode();
            self.position_in_memory += 2; //自增程序计数，指向下一条指令

            let c = ((opcode & 0xF000) >> 12) as u8;
            let x = ((opcode & 0x0F00) >> 8) as u8;
            let y = ((opcode & 0x00F0) >> 4) as u8;
            let d = ((opcode & 0x000F) >> 0) as u8;

            let nnn = opcode & 0x0FFF;
            match (c, x, y, d) {
                (0, 0, 0, 0) => {
                    return; //遇到操作码0x0000时，此处短路功能终止函数执行
                }
                (0, 0, 0xE, 0xE) => self.ret(),
                (0x2, _, _, _) => self.call(nnn),
                (0x8, _, _, 0x4) => self.add_xy(x, y), //匹配元组，分配到“硬件电路”
                _ => todo!("opcode {:04x}", opcode),   //需要补充
            }
        }
    }

    fn add_xy(&mut self, x: u8, y: u8) {
        let arg1 = self.registers[x as usize];
        let arg2 = self.registers[y as usize];

        let (val, overflow) = arg1.overflowing_add(arg2); //如果溢出，返回的bool为true
        self.registers[x as usize] = val;

        if overflow {
            self.registers[0xF] = 1;
        } else {
            self.registers[0xF] = 0;
        }
    }

    fn call(&mut self, addr: u16) {
        let sp = self.stack_pointer;
        let stack = &mut self.stack;

        if sp > stack.len() {
            panic!("Stack overflow!");
        }
        stack[sp] = self.position_in_memory as u16; //把当前值入栈
        self.stack_pointer += 1; //自增栈指针
        self.position_in_memory = addr as usize; //跳转地址
    }

    fn ret(&mut self) {
        if self.stack_pointer == 0 {
            panic!("Stack underflow");
        }
        self.stack_pointer -= 1;
        let call_addr = self.stack[self.stack_pointer];
        self.position_in_memory = call_addr as usize;
    }
}
fn main() {
    let mut cpu = CPU {
        registers: [0; 16],
        memory: [0; 4096],
        position_in_memory: 0,
        stack: [0; 16],
        stack_pointer: 0,
    };

    //初始化寄存器

    cpu.registers[0] = 5;
    cpu.registers[1] = 10;

    let mem = &mut cpu.memory;
    mem[0x000] = 0x21;
    mem[0x001] = 0x00; //设置操作码0x2100:在0x100处调用函数。
    mem[0x002] = 0x21;
    mem[0x003] = 0x00; //设置操作码0x2100:同样在0x100处调用函数。
    mem[0x004] = 0x00;
    mem[0x005] = 0x00; //设置操作码0x0000:停止函数

    mem[0x100] = 0x80;
    mem[0x101] = 0x14; //0x8014 把寄存器2的值加到寄存器0上
    mem[0x102] = 0x80;
    mem[0x103] = 0x14; //0x8014 把寄存器2的值加到寄存器0上
    mem[0x104] = 0x00;
    mem[0x105] = 0xEE; //0x00EE 函数返回

    cpu.run();

    assert_eq!(cpu.registers[0], 45);
    println!("5 + (10*2) +(10*2) = {}", cpu.registers[0]);
}
